﻿/* 
 *  <copyright file="OnGroundCmp.cs" company="Wesley Jowitt">
 *  
 *  SobrietyEngine: Copyright (c) 2011 Wesley Jowitt
 *  
 *  This software is distributed under a software license.
 *   * As of the creation time of this file, the license in use is the Microsoft Permissive License (Ms-PL) 
 *    * The license can be viewed at:
 *     * License.Txt in the root folder of this project
 *      * http://sobrietyengine.codeplex.com/license
 *       * http://www.microsoft.com/opensource/licenses.mspx
 *       
 *  </copyright>
 *   */

using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;

using SobrietyEngine.Common;
using SobrietyEngine.Actor;
using SobrietyEngine.Components.Physics;
using SobrietyEngine.Logging;

using FarseerPhysics;
using FarseerPhysics.Collision;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;

namespace SobrietyEngine.Components.Physics.OnCollision
{
    public class OnGroundCmp:OnCollisionBase, SobrietyEngine.Components.IComponent
    {
        public Boolean IsOnGround { get; set; }
        //public List<Contact> Contacts { get; set; }
        public List<IActor> Actors { get; set; }

        public Vector2 PreviousContactAvg { get; set; }

        public Vector2 AvgOfContacts { get; set; }
        int numContacts = 0;
        public Vector2 contactTotal = Vector2.Zero;
        public List<Vector2> Normals { get; set; }

        public Int32 TimeOnGroundMs { get; set; }
        public Int32 TimeOffGroundMs { get; set; }


        /// <summary>
        /// Gets or sets the direction from the player that ground will be found
        /// so that if we hit static geometry above us, it doesnt count as 'on ground'
        /// </summary>
        /// <value>
        /// The ground direction.
        /// </value>
        public Vector2 GroundNormal { get; set; }

        Category CollisionCatOfGround = 0;

        public OnGroundCmp()
            : base()
        {
            //Contacts = new List<Contact>();
            Normals = new List<Vector2>();
            Actors = new List<IActor>();
            AvgOfContacts = Vector2.Zero;
            this.enableCollisionResponse = true;
            this.enableSeperationResponse = true;
            GroundNormal = new Vector2(0, 1);
        }

        public OnGroundCmp(Category collisionCatagoryOfGround)
            : this()
        {
            CollisionCatOfGround = collisionCatagoryOfGround;
        }

        public override void Update(GameTime ElapsedTime)
        {
            if (IsOnGround)
            {
                TimeOffGroundMs = 0;
                TimeOnGroundMs += ElapsedTime.ElapsedGameTime.Milliseconds;
            }
            else
            {
                TimeOnGroundMs = 0;
                TimeOffGroundMs += ElapsedTime.ElapsedGameTime.Milliseconds;
            }
        }

        override public bool OnCollision(Fixture me, Fixture other, Contact contact)
        {
            if ((other.CollisionCategories | CollisionCatOfGround) == 0)
                return true;            

            
            Vector2 normal;
            FarseerPhysics.Common.FixedArray2<Vector2> fa = new FarseerPhysics.Common.FixedArray2<Vector2>();
            contact.GetWorldManifold(out normal, out fa);

            float dot = Vector2.Dot(GroundNormal, normal);
            if (dot <= 0)
                return true; //we are not on the ground

           // Contacts.Add(contact);
            IsOnGround = true;

            Normals.Add(normal);

            numContacts++;
            contactTotal += normal;
            PreviousContactAvg = AvgOfContacts;
            AvgOfContacts = contactTotal / numContacts;

            
            IActor oa = (IActor)other.Body.UserData;
            Actors.Add(oa);

            return true;
        }

        public override void OnSeperation(Fixture me, Fixture other)
        {
            if ((other.CollisionCategories | CollisionCatOfGround) == 0)
                return;

            if (numContacts == 0)
            {
                IsOnGround = false;
                return;
            }

            //get actor and normal
            IActor oa = (IActor)other.Body.UserData;
            int ind = Actors.IndexOf(oa);
            if (ind == -1)
                return;

            //this wasnt an actor we were tracking

            numContacts--;
            if (numContacts == 0)
                IsOnGround = false;
            Vector2 normal = Normals[ind];

            //remove normal from our average
            PreviousContactAvg = AvgOfContacts;
            contactTotal -= normal;
            if (numContacts != 0)
                AvgOfContacts = contactTotal / numContacts;
            else
                AvgOfContacts = Vector2.Zero;

            //remove objects from lists
            Actors.Remove(oa);
            Normals.Remove(normal);
        }
    }
}
